﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Zarion_Quest.Base
{
    class AnimatedObject2D : Object2D
    {
        /// <summary>
        /// Dicionário contendo as animações do objeto
        /// </summary>
        private Dictionary<string, Animation> animations;
        public Dictionary<string, Animation> Animations
        {
            get { return this.animations; }
        }

        /// <summary>
        /// Nome da animação em execução
        /// </summary>
        private string animationKey;
        public string AnimationKey
        {
            get { return this.animationKey; }
        }

        /// <summary>
        /// Indice do frame da animação em execução
        /// </summary>
        private int frameIndex;
        public int FrameIndex
        {
            get { return this.frameIndex; }
        }

        /// <summary>
        /// Tempo decorrido do frame atual da animação
        /// </summary>
        private float timeElapsed;

        /// <summary>
        /// Lista com os quadros do sprite sheet
        /// </summary>

        /// <summary>
        /// Largura do quadro
        /// </summary>
        private int frameWidth;
        public int FrameWidth
        {
            get { return frameWidth; }
        }

        /// <summary>
        /// Colunas do sprite sheet padrão
        /// </summary>
        private int columns;

        /// <summary>
        /// Linhas do sprite sheet padrão
        /// </summary>
        private int rows;

        /// <summary>
        /// Altura do quadro
        /// </summary>
        private int frameHeight = 0;
        public int FrameHeight
        {
            get { return frameHeight; }
        }

        /// <summary>
        /// Tamanho do frame
        /// </summary>
        public Vector2 Size
        {
            get { return new Vector2(frameWidth, frameHeight); }
        }

        /// <summary>
        /// Construtor do objeto animado
        /// </summary>
        /// <param name="texture">Imagem (Sprite Sheet)</param>
        /// <param name="columns">Quantidade de colunas</param>
        /// <param name="rows">Quantidade de linhas</param>
        public AnimatedObject2D(Texture2D texture, int columns, int rows)
            : base(texture)
        {
            Position = new Vector2();
            animations = new Dictionary<string, Animation>();
            frameIndex = 0;
            timeElapsed = 0.0f;
            Texture = texture;
            this.columns = columns;
            this.rows = rows;
        }
        /// <summary>
        /// Calcular o retangulo apenas do que está visivel...
        /// TODO ver se o override daqui funciona quando chamado pelo próprio Object2D, eg: no método hitTestObject
        /// </summary>
        public override void calculateRectangle()
        {
            CollisionBounds = new Rectangle((int)(Position.X - (MiddlePoint.X * ScaleVector.X)),
                (int)(Position.Y - (MiddlePoint.Y * ScaleVector.Y)),
                (int)(frameWidth * ScaleVector.X),
                (int)(frameHeight * ScaleVector.Y));
        }
        
        /// <summary>
        /// Atualiza a animação do objeto
        /// </summary>
        /// <param name="gameTime">Tempo de jogo</param>
        public virtual void Update(GameTime gameTime)
        {
            timeElapsed += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (timeElapsed > animations[AnimationKey].Interval)
            {
                if (animations[AnimationKey].IsLooping)
                    frameIndex = (frameIndex + 1) % animations[AnimationKey].FramesCount;
                else
                    frameIndex = (int)MathHelper.Min(frameIndex + 1, animations[AnimationKey].FramesCount - 1);
                timeElapsed = 0.0f;
            }
        }

        /// <summary>
        /// Desnha o objeto animado
        /// </summary>
        /// <param name="spriteBatch"></param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(Animations[AnimationKey].Texture,
                Position,
                Animations[AnimationKey].Frames[frameIndex],
                ObjectColor * Alpha,
                MathHelper.ToRadians(Rotation),
                MiddlePoint,
                ScaleVector,
                ObjectSpriteEffects,
                LayerDepth);
        }

        /// <summary>
        /// Adiciona novas animações
        /// </summary>
        /// <param name="name">Nome chave da animação</param>
        /// <param name="newAnimation">Animação definida</param>
        private void AddAnimation(string name, Animation newAnimation)
        {
            animations.Add(name, newAnimation);
        }

        /// <summary>
        /// Inicia ou continua uma animação.
        /// </summary>
        /// <param name="name">Nome da animação no dicionário</param>
        public void PlayAnimation(string name)
        {
            // Foolprofing...
            if (!Animations.ContainsKey(name))
                Console.WriteLine("Chave de animação \"" + name + "\" não encontrada no dicionário de animações!");
            else
            {
                // Se a animação for a mesma em execução, não reinicia a animação
                if (name == AnimationKey)
                    return;

                // Inicia uma nova animação
                this.animationKey = name;
                this.frameIndex = 0;
                this.timeElapsed = 0.0f;
                // A animação tem pelo menos 1 frame, então é relativamente seguro fazer isso
                this.frameHeight = Animations[AnimationKey].Frames[0].Height;
                this.frameWidth = Animations[AnimationKey].Frames[0].Width;
                MiddlePoint = Animations[AnimationKey].MiddlePoint;
            }
        }

        /// <summary>
        /// Adiciona uma nova sprite sheet para facilitar a animação
        /// </summary>
        /// <param name="texture">Imagem (Sprite Sheet)</param>
        /// <param name="columns">Quantidade de colunas</param>
        /// <param name="rows">Quantidade de linhas</param>
        /// <param name="animationName">Nome da animação no dicionário</param>
        /// <param name="startFrame">Frame inicial</param>
        /// <param name="framesCount">Total de frames da animação</param>
        /// <param name="interval">Tempo entre cada frame</param>
        /// <param name="isLooping">Indica se a animação vai se repetir ou parar no último quadro</param>
        public void addNewAnimation(Texture2D texture, int columns, int rows, string animationName, int startFrame, int framesCount, float interval, bool isLooping)
        {
            int animFrameHeight = texture.Height / rows;
            int animFrameWidth = texture.Width / columns;

            // Cria todos os quadros da imagem
            List<Rectangle>  animFrames = new List<Rectangle>();
            for (int i = 0; i < rows; i++)
                for (int j = 0; j < columns; j++)
                    animFrames.Add(new Rectangle(j * animFrameWidth, i * animFrameHeight, animFrameWidth, animFrameHeight));

            Vector2 animMiddlePoint = new Vector2(animFrameWidth / 2, animFrameHeight / 2);

            Animation anim = new Animation(texture, animFrames, animMiddlePoint, startFrame, framesCount, interval);
            anim.IsLooping = isLooping;
            AddAnimation(animationName, anim);
        }

        /// <summary>
        /// Cria uma nova animação utilizando o sprite sheet padão
        /// </summary>
        /// <param name="animationName">Nome da animação no dicionário</param>
        /// <param name="startFrame">Frame inicial</param>
        /// <param name="framesCount">Total de frames da animação</param>
        /// <param name="interval">Tempo entre cada frame</param>
        /// <param name="isLooping">Indica se a animação vai se repetir ou parar no último quadro</param>
        public void addNewAnimation(string animationName, int startFrame, int framesCount, float interval, bool isLooping)
        {
            addNewAnimation(Texture, columns, rows, animationName, startFrame, framesCount, interval, isLooping);
        }
    }
}